package com.by.toolkitspring.dubbo.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson2.JSON;
import com.by.toolkit.consts.ByContextConst;
import com.by.toolkit.exceptions.ServiceException;
import com.by.toolkit.platform.ByContext;
import com.by.toolkitspring.dubbo.custom.CustomAttachmentSupport;
import com.by.toolkitspring.dubbo.filter.spi.RpcParamSpi;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.Collections;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.Collectors;

import static com.by.toolkit.consts.ByContextConst.FILTER_RPC_LIST;
import static com.by.toolkit.utils.ExceptionUtil.getStackTrace;

/**
 * 服务发起对其他服务对调用为消费者 消费这进行属性注入,提供者进行属性提取并存储
 * 当自己当服务被调用为服务提供者
 *
 * @author Tian Wei
 * @version 1.0
 * @date 2023/12/29 6:25 PM
 */
@Slf4j
@Activate(group = {CommonConstants.CONSUMER, CommonConstants.PROVIDER})
public class ByDubboContextInjectionFilter implements Filter, BaseFilter.Listener {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            boolean isConsumerSide = RpcContext.getServiceContext().isConsumerSide();
            if (isConsumerSide) {
                setCustomerSideContext(invocation);
            }
            //判断当前dubbo服务是否为服务提供者
            boolean isProviderSide = RpcContext.getServiceContext().isProviderSide();
            if (isProviderSide) {
                setProviderSideContext(invocation);
            }
            return invoker.invoke(invocation);
        } catch (ServiceException e) {
            return AsyncRpcResult.newDefaultAsyncResult(e, invocation);
        } catch (Exception e) {
            log.info("[ByDubboContextInjectionFilter][invoke] 非运行时异常:{}", getStackTrace(e));
            return AsyncRpcResult.newDefaultAsyncResult(e, invocation);
        }
    }

    @Override
    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {

        //如果是提供者,则需要返回参数
        boolean isProviderSide = RpcContext.getServiceContext().isProviderSide();
        if (isProviderSide) {
            clearLocalData();
            if (log.isDebugEnabled()) {
                log.debug("[ByDubboContextInjectionFilter][onResponse] 服务提供者清除本地缓存,服务名称:{},方法名称:{}", invocation.getServiceName(), invocation.getMethodName());
            }
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug("[ByDubboContextInjectionFilter][onResponse] 服务消费者不清除本地缓存,服务名称:{},方法名称:{}", invocation.getServiceName(), invocation.getMethodName());
        }
    }

    @Override
    public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {

        //如果是提供者,则需要返回参数
        boolean isProviderSide = RpcContext.getServiceContext().isProviderSide();
        if (isProviderSide) {
            clearLocalData();
            if (log.isDebugEnabled()) {
                log.debug("[ByDubboContextInjectionFilter][onResponse] 服务提供者清除本地缓存,服务名称:{},方法名称:{}", invocation.getServiceName(), invocation.getMethodName());
            }
            return;
        }
        if (log.isDebugEnabled()) {
            log.debug("[ByDubboContextInjectionFilter][onResponse] 服务消费者不清除本地缓存,服务名称:{},方法名称:{}", invocation.getServiceName(), invocation.getMethodName());
        }
    }

    /**
     * 服务提供者进行上下文提取
     *
     * @return
     */
    private static void setProviderSideContext(Invocation invocation) {
        RpcContextAttachment serverAttachment = RpcContext.getServerAttachment();
        Map<String, String> paramMap = MapUtil.defaultIfEmpty(serverAttachment.getObjectAttachments(), Collections.emptyMap())
                .entrySet()
                .stream()
                .filter(entry -> FILTER_RPC_LIST.contains(entry.getKey()) || entry.getKey().startsWith(ByContextConst.INVALID_PREFIX))
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString()));
        try {
            Map<String, CustomAttachmentSupport> beanMap = SpringUtil.getBeansOfType(CustomAttachmentSupport.class);
            if (CollUtil.isNotEmpty(beanMap)) {
                beanMap.forEach((name, support) -> {
                    Map<String, String> customAttachment = support.customAttachment();
                    log.info("[ByDubboContextInjectionFilter][setProviderSideContext] customAttachment:[{}]", customAttachment);
                    if (CollUtil.isNotEmpty(customAttachment)) {
                        paramMap.putAll(customAttachment);
                    }
                    log.info("[ByDubboContextInjectionFilter][setProviderSideContext] clientAttachment:[{}]", paramMap);
                });
            }
        } catch (Exception e) {
            log.warn("[ByDubboContextInjectionFilter][setCustomerSideContext] hasException:[{}] ", getStackTrace(e));
        }

        ByContext.putAll(paramMap);
        if (log.isDebugEnabled()) {
            log.debug("[ByDubboContextInjectionFilter][setProviderSideContext] 服务名称:{},方法名称:{},setProviderSideContext:{}", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(paramMap));
        }
        acquireContext(serverAttachment);
    }

    /**
     * 消费者进行上下文注入
     *
     * @return
     */
    private static void setCustomerSideContext(Invocation invocation) {
        Map<String, String> paramMap = ByContext.getAll();
        RpcContextAttachment clientAttachment = RpcContext.getClientAttachment();
        if (!paramMap.isEmpty()) {
            paramMap.forEach((key, val) -> {
                clientAttachment.setAttachment(key, val);
                clientAttachment.setObjectAttachment(key, val);
            });
        }
        if (log.isDebugEnabled()) {
            log.debug("[ByDubboContextInjectionFilter][setCustomerSideContext] 服务名称:{},方法名称:{},setCustomerSideContext:{}", invocation.getServiceName(), invocation.getMethodName(), JSON.toJSONString(paramMap));
        }
        acquireContext(clientAttachment);
    }

    private static void clearLocalData() {
        ByContext.remove();
    }

    private static void acquireContext(RpcContextAttachment attachment) {
        ServiceLoader<RpcParamSpi> serviceLoader = ServiceLoader.load(RpcParamSpi.class);
        Map<String, String> paramValMap = Maps.newHashMap();
        serviceLoader.forEach(spi -> {
            String paramName = spi.paramName();
            String attVal = attachment.getAttachment(paramName);
            paramValMap.put(paramName, attVal);
            boolean required = spi.required();
            if (required) {
                String ignoreTenant = attachment.getAttachment(ByContextConst.IGNORE_TENANT);
                if (StringUtils.isBlank(attVal) && StringUtils.isBlank(ignoreTenant)) {
                    throw new ServiceException(StrUtil.format("rpc上下文[{}]不存在", paramName));
                }
            }
            if (StringUtils.isNotEmpty(attVal)) {
                spi.receiveVal(attVal);
            }

        });
        if (log.isDebugEnabled()) {
            log.debug("[ByDubboContextInjectionFilter][acquireContext] 服务名称:{},方法名称:{},rpc名称{}:参数:{}", attachment.getInterfaceName(), attachment.getMethodName(), attachment.getClass().getSimpleName(), JSON.toJSONString(paramValMap));
        }
    }
}
